package com.ow.dva.config;

import cn.hutool.json.JSONUtil;
import com.ow.dva.config.constant.CacheConstant;
import com.ow.dva.module.base.entity.param.DvaPage;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * 描述 :Redis缓存配置
 * 包名 : com.ow.dva.config
 *
 * @author GaoXiang
 * @version V1.1
 * @since 19-1-8
 */
@Configuration
@EnableCaching
@EnableRedisHttpSession
public class RedisCacheManagerConfiguration extends CachingConfigurerSupport{

    //private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    LettuceConnectionFactory lettuceConnectionFactory;

    //设置一些默认的配置
    private RedisCacheConfiguration DVA_CACHE = createCacheConfiguration(60*12, CacheConstant.CACHE);
    private RedisCacheConfiguration DVA_CACHE_1M = createCacheConfiguration(1, CacheConstant.CACHE_1M);
    private RedisCacheConfiguration DVA_CACHE_5M = createCacheConfiguration(5, CacheConstant.CACHE_5M);
    private RedisCacheConfiguration DVA_CACHE_10M = createCacheConfiguration(10, CacheConstant.CACHE_10M);
    private RedisCacheConfiguration DVA_CACHE_30M = createCacheConfiguration(30, CacheConstant.CACHE_30M);
    private RedisCacheConfiguration DVA_CACHE_60M = createCacheConfiguration(60, CacheConstant.CACHE_60M);

    /**
     * 自定义cacheManager
     * 使用需要预先配置好 spring.redis 下的一些信息
     * @return cacheManager实例
     */
    @Bean
    @Override
    public CacheManager cacheManager() {
        // 创建RedisCacheWriter对象 不锁
        RedisCacheWriter redisCacheWriter = RedisCacheWriter
                .nonLockingRedisCacheWriter(lettuceConnectionFactory);
        // 设置自定义缓存配置项
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = redisCacheConfiguration();
        //初始化 RedisCacheManager
        return new RedisCacheManager(redisCacheWriter, DVA_CACHE_1M, redisCacheConfigurationMap);
    }


    /**
     * redis 自定义key生成策略
     * target: 类
     * method: 方法
     * params: 参数
     * @return 生成的自定义key
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {

        return (target, method, params) ->
                    CacheConstant.CACHE_KEY_SEPARATOR +
                    getTargetName(target) +
                    CacheConstant.CACHE_KEY_SEPARATOR +
                    method.getName() +
                    CacheConstant.CACHE_KEY_SEPARATOR +
                    getParamValue(params);
    }

    /**
     * params转换为json字符串
     * @param params 参数集合
     * @return 自定义字符串
     */
    public String getParamValue(Object... params){
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < params.length; i++) {
            if (i != 0 ) sb.append(";");
            if(null != params[i]){
                if(params[i] instanceof DvaPage){
                    sb.append("dvaPage");
                }else{
                    sb.append(JSONUtil.toJsonStr(params[i]).replaceAll(":","="));
                }
            }else{
                sb.append("null");
            }
        }
        sb.append("]");
        return sb.toString();
    }

    /**
     * 组合targetName 过滤 Spring CGLIB 代理时添加的 $$EnhancerBySpringCGLIB$$ 字样和内存地址
     * @param target 类信息
     * @return 类字符串
     */
    public String getTargetName(Object target){
        String targetName = target.getClass().getName();
        int indexLength = targetName.indexOf(CacheConstant.SPRING_CGLIB_SUFFIX);
        if(indexLength > 0){
            targetName = targetName.substring(0,indexLength);
        }
        return targetName;
    }

    /**
     * RedisTemplate配置 在单独使用redisTemplate的时候 重新定义序列化方式
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        // 设置序列化
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        redisTemplate.setKeySerializer(stringSerializer);// key序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
        redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 配置缓存超时设置
     * @return 超时设置集
     */
    private Map<String, RedisCacheConfiguration> redisCacheConfiguration(){
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        redisCacheConfigurationMap.put(CacheConstant.CACHE, DVA_CACHE);
        redisCacheConfigurationMap.put(CacheConstant.CACHE_1M, DVA_CACHE_1M);
        redisCacheConfigurationMap.put(CacheConstant.CACHE_5M, DVA_CACHE_5M);
        redisCacheConfigurationMap.put(CacheConstant.CACHE_10M, DVA_CACHE_10M);
        redisCacheConfigurationMap.put(CacheConstant.CACHE_30M, DVA_CACHE_30M);
        redisCacheConfigurationMap.put(CacheConstant.CACHE_60M, DVA_CACHE_60M);
        return redisCacheConfigurationMap;
    }

    /**
     * 配置快捷创建缓存配置项工具
     * 本配置默认空值一样需要缓存,因为需要缓存的大部分是查询费时,而不是显示结果费时.
     * 缓存指定了以分钟为单位的有效期,可以根据资源更新频率酌情选择
     * @param minutes 有效期 分钟
     * @param name 缓存名称
     * @return 缓存配置项
     */
    private RedisCacheConfiguration createCacheConfiguration(Integer minutes,String name){
        return RedisCacheConfiguration
                .defaultCacheConfig()
                // 超时时间
                .entryTtl(Duration.ofMinutes(minutes))
                // 设置Value序列化方式为 Jackson2Json
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new GenericJackson2JsonRedisSerializer())
                )
                // 缓存名称(前缀)
                .prefixKeysWith(name);
    }

}
